package PrjHttpWs

import (
	"dgo/goutils/utils"
	"fmt"
	"strings"
	"sync/atomic"
	"time"

	"golang.org/x/net/websocket"
)

type WsClient struct {
	startflag     int32
	pingktaskflag int32
	closeflag     int32
	objDNA        uint64

	LogType               string
	ws_req_url            string
	origin_url            string
	connected             int
	connected_remote_addr string // 连接的远程地址

	terminated       int
	conn             *WsConnEx
	conn_n           int
	dismsg           string
	Monitor_SendSize *utils.MonitorSpeedSizeRec
	Monitor_RecvSize *utils.MonitorSpeedSizeRec
	OnSent           func(sender *WsClient, msgtype byte, data []byte)
	OnSendErr        func(sender *WsClient, msgtype byte, msg []byte, err error)
	OnRecv           func(sender *WsClient, msgtype byte, data []byte)
	OnDis            func(sender *WsClient)
	OnConnect        func(sender *WsClient)
	OnConnectErr     func(sender *WsClient, err error)
}

func NewWsClient(ws_req_url, origin_url string) *WsClient {
	rval := &WsClient{
		ws_req_url: ws_req_url,
		origin_url: origin_url,
		objDNA:     utils.NewObjectDNA64(),
	}
	return rval
}

func (this *WsClient) RemoteAddr() string {
	return this.connected_remote_addr
}

func (this *WsClient) checkConnect() bool {
	if this.conn != nil {
		return true
	}

	if len(this.ws_req_url) == 0 {
		this.connected_remote_addr = ""
		if this.OnConnectErr != nil {
			this.OnConnectErr(this, fmt.Errorf("缺少连接参数requrl"))
		} else {
			utils.Log.DebugTypef(this.LogType, "缺少连接参数requrl")
		}
		return false
	}

	con, err := websocket.Dial(this.ws_req_url, "", this.origin_url)
	if err != nil {
		this.connected_remote_addr = ""
		if this.OnConnectErr != nil {
			this.OnConnectErr(this, err)
		} else {
			utils.Log.DebugTypef(this.LogType, "连接服务器(%s)异常:%s", this.ws_req_url, err.Error())
		}
		return false
	}
	this.conn = NewWsConnEx(con)
	this.connected_remote_addr = con.RemoteAddr().String()
	if this.OnConnect != nil {
		this.OnConnect(this)
	}
	this.conn_n++
	this.closeflag = 0
	this.connected = 1
	return true
}

func (this *WsClient) IsConnected() bool {
	return this.connected == 1
}

func (this *WsClient) RequestDisconnect(msg string) {
	conn := this.conn
	if conn == nil {
		return
	}
	if atomic.CompareAndSwapInt32(&this.closeflag, 0, 1) {
		this.dismsg = fmt.Sprintf("[%s]%s", utils.NowString(), msg)
	}
	conn.Close()
}

func (this *WsClient) onClose() {
	conn := this.conn
	if conn == nil {
		return
	}

	if this.connected == 0 {
		return
	}

	if this.OnDis != nil {
		this.OnDis(this)
	}
	this.connected = 0
	this.conn = nil
}

/*
**

	进行PING, 检测PONG,超时断线
*/
func (this *WsClient) CheckStartPingTask() {
	if atomic.CompareAndSwapInt32(&this.pingktaskflag, 0, 1) {
		utils.AddTaskFxiedInterval(fmt.Sprintf("%d_WS_PING", this.objDNA), time.Second*20, func() bool {
			if this.connected == 1 && this.conn != nil {
				utils.DefaultWorkers().PostTaskFunc(this, func() { // 确保单线程操作
					conn := this.conn
					if conn != nil {
						conn.SendPingMessage()
					}
				})
			}
			return this.pingktaskflag == 1
		})

		utils.AddTaskFxiedInterval(fmt.Sprintf("%d_WS_PONG", this.objDNA), time.Second*30, func() bool {
			conn := this.conn
			if conn == nil {
				return this.pingktaskflag == 1
			}
			if this.connected == 1 && conn != nil {
				if conn.CheckTimeOut(time.Second * 30) {
					this.RequestDisconnect("PONG超时断线")
				}
			}
			return this.pingktaskflag == 1
		})
	}

}

func (this *WsClient) Stop() {
	this.terminated = 1
	this.pingktaskflag = 0
	this.RequestDisconnect("停止服务")
}

func (this *WsClient) WaitFor(timeout time.Duration) bool {
	t1 := time.Now()
	for {
		if this.startflag == 0 {
			return true
		}
		if time.Now().Sub(t1) >= timeout {
			return false
		}
		time.Sleep(time.Millisecond * 100)
	}
}

func (this *WsClient) GetWsReqUrl() string {
	return this.ws_req_url
}

func (this *WsClient) ConfigWsReqUrl(ws_req_url, origin_url string) bool {
	isChanged := false
	if this.ws_req_url != ws_req_url {
		this.ws_req_url = ws_req_url
		isChanged = true
	}

	if len(origin_url) == 0 {
		protocol := "http://"
		tmpUrl := ws_req_url
		if strings.HasPrefix(tmpUrl, "ws://") {
			tmpUrl = strings.TrimPrefix(tmpUrl, "ws://")
		} else if strings.HasPrefix(tmpUrl, "wss://") {
			tmpUrl = strings.TrimPrefix(tmpUrl, "wss://")
			protocol = "https://"
		}
		strs := strings.SplitN(tmpUrl, "/", 2)
		origin_url = protocol + strs[0]

		if this.origin_url != origin_url {
			this.origin_url = origin_url
			isChanged = true
		}
	}
	if isChanged {
		this.RequestDisconnect("连接参数改变")
	}

	return isChanged
}

func (this *WsClient) Start() {
	if !atomic.CompareAndSwapInt32(&this.startflag, 0, 1) {
		utils.Log.WarnTypef(this.LogType, "%s已经启动,不要重复开启", this.ws_req_url)
	}
	defer func() {
		this.startflag = 0
	}()
	this.terminated = 0
	for {
		if this.terminated == 1 {
			this.onClose()
			break
		}

		if !this.checkConnect() {
			time.Sleep(time.Second * 10)
			continue
		}

		msgtype, msg, err := this.conn.ReceiveMessage()
		if err != nil {
			this.RequestDisconnect(fmt.Sprintf("接收出现异常:%s", err.Error()))
			this.onClose()
			continue
		}

		lvMonitor := this.Monitor_RecvSize
		if lvMonitor != nil {
			lvMonitor.Delta(int64(len(msg)))
		}

		if this.OnRecv != nil {
			this.OnRecv(this, msgtype, msg)
		}
	}
}

/*
**

	支持多线程
*/
func (this *WsClient) SendMsg(msgtype byte, msg []byte) error {

	conn := this.conn
	if conn == nil {
		return fmt.Errorf("尚未建立连接...")
	}
	utils.DefaultWorkers().PostTaskFunc(this, func() {
		err := conn.SendWsMessage(msgtype, msg)
		if err != nil {
			if this.OnSendErr != nil {
				this.OnSendErr(this, msgtype, msg, err)
			}
			this.RequestDisconnect(fmt.Sprintf("发送数据出现异常:%s", err.Error()))
		}
		lvMonitor := this.Monitor_SendSize
		if lvMonitor != nil {
			lvMonitor.Delta(int64(len(msg)))
		}
		if this.OnSent != nil {
			this.OnSent(this, msgtype, msg)
		}
	})
	return nil
}

//func (this *WsClient) receive() (msgtype byte, msg []byte, err error) {
//	return this.conn.ReceiveMessage()
//}
